<template>
  <div class="network-wrap">
    <div :id="id" :ref="id" class="network" :style="networkStyle" />
    <div
      v-if="showEagelEye"
      v-show="fullScreen"
      :id="`${id}EagelEyeVis`"
      :ref="`${id}EagelEyeVis`"
      class="eagelEyeVis"
      :style="eagleEyeStyle"
    />
    <div
      v-for="room in roomData"
      :id="room.roomId"
      :key="room.roomId"
      :style="{
        top: room.y,
        left: room.x,
        width: room.width,
        height: room.height
      }"
      class="room-box text-center"
    >
      <span class="font-small">{{ room.roomName }}</span>
    </div>
  </div>
</template>
<script>
  import { mapGetters } from 'vuex'
  import RenderVis from './js/renderVis'
  import { colorData, categoryNode, nodeColor } from './js/format'
  export default {
    props: {
      id: {
        type: String,
        default: 'myNetwork'
      },
      nodes: {
        type: Array,
        default: () => []
      },
      edges: {
        type: Array,
        default: () => []
      },
      options: {
        type: Object,
        default: () => {}
      },
      showEagelEye: {
        type: Boolean,
        default: true
      },
      isEdit: {
        type: Boolean,
        default: false
      },
      topoType: {
        type: String,
        default: 'topo'
      },
      operateType: {
        type: String,
        default: ''
      },
      staticsItem: {
        type: Object,
        default: () => {
          return {
            width: 0,
            height: 0
          }
        }
      }
    },
    data() {
      return {
        // myNetwork: null, // 拓扑图实例
        myNode: null, // 拓扑图节点实例
        myEdge: null, // 拓扑图边实例
        eagelEyeVis: null, // 鹰眼拓扑图实例
        myEagelEdge: null,
        scale: 0.2, // 拓扑图和鹰眼初始比例
        dragCode: 0, // 记录当前鼠标拖拽的是哪个拓扑，0-无拖拽 1-拓扑图拖拽 2-鹰眼拖拽
        eagleEyeStyle: {},
        clientX: 0, // 鼠标距离屏幕左侧距离
        clientY: 0, // 鼠标距离屏幕顶部距离
        network_offset_left: 0, // 画布距离屏幕左侧的距离
        network_offset_top: 0, // 画布距离屏幕顶部的距离
        networkStyle: {},
        roomData: [],
        roomList: [],
        roomScale: 1
      }
    },
    computed: {
      ...mapGetters([
        'baseRegionList',
        'regionStationTree',
        'stationSelected',
        'gisActived',
        'cableLevelSelected',
        'customDictList',
        'hideAlarmLevel',
        'fullScreen'
      ]),
      dictCableLevelList() {
        return this.customDictList.filter(i => i.type === 'CABLE_LEVEL')
      }
    },
    watch: {
      nodes: {
        handler(newVal, oldVal) {
          if (this.isEdit) return
          this.$nextTick(() => {
            this.render(true)
          })
        },
        immediate: true,
        deep: true
      },
      stationSelected() {
        this.showNodes()
      },
      cableLevelSelected() {
        this.showFibers()
      },
      gisActived(newVal, oldVal) {
        console.log('高亮状态改变', newVal)
        this.highLightStaionAndFiber(this.gisActived)
      },
      fullScreen(newVal) {
        if (!newVal) return
        this.$nextTick(() => {
          const { x, y } = this.myNetwork.getViewPosition()
          this.eagelEyeVis.moveTo({
            position: {
              x: x,
              y: y
            }
          })
        })
      },
      hideAlarmLevel(newVal) {
        if (this.isEdit || this.topoType !== 'topo') return
        this.showNodes()
      }
    },
    mounted() {
      this.$nextTick(() => {
        this.init()
        window.addEventListener('resize', this.init)
      })
    },
    beforeDestroy() {
      this.destroy()
      window.removeEventListener('resize', this.init)
    },
    methods: {
      init() {
        const containBox = document.getElementById(this.id)
        if (!containBox) return
        const myNetworkWrap = containBox.parentNode // 画布容器DOM
        // 计算拓扑图的高度
        this.networkStyle = {
          height: myNetworkWrap.offsetHeight + 'px'
        }
        this.eagleEyeStyle = {
          width: myNetworkWrap.offsetWidth * this.scale + 'px',
          height: myNetworkWrap.offsetHeight * this.scale + 'px'
        }
        this.network_offset_top = myNetworkWrap.offsetTop + 40 + 80 // Y偏移量
        console.log(this.network_offset_top)
        // 必须要加延时函数，等待鹰眼的宽高设置完以后再去渲染拓扑图，不然两个拓扑图会存在位置偏差
        setTimeout(() => {
          this.render()
        })
      },
      render(showNodes = false) {
        this.destroy()
        this.$emit('disableEdit')
        if (this.$refs[this.id]) {
          const vis_instance1 = new RenderVis(this.$refs[this.id], this.nodes, this.edges)
          vis_instance1.init(this.options)
          this.myNetwork = vis_instance1.vis_network_instance
          this.myNode = vis_instance1.vis_node_instance
          this.myEdge = vis_instance1.vis_edge_instance
          this.getRoomList()
        }
        if (this.$refs[`${this.id}EagelEyeVis`]) {
          const vis_instance2 = new RenderVis(
            this.$refs[`${this.id}EagelEyeVis`],
            this.nodes,
            this.edges
          )
          vis_instance2.init()
          this.eagelEyeVis = vis_instance2.vis_network_instance
          this.myEagelEdge = vis_instance2.vis_edge_instance
          this.eagelEyeVis.setOptions({
            interaction: {
              dragNodes: false
            }
          })
        }
        this.eagelEyeVis && this.handleDrag() // 拓扑和鹰眼拖拽时互动
        this.eagelEyeVis && this.handleZoom() // 缩放联动
        this.edgesHover() // 连接线hover
        this.edgesClick() // 连接线click
        this.nodesClick() // 节点click
        this.nodesHover()
        this.setEdgeSmooth() // 设置连线的弯曲度
        // 校准
        if (this.topoType === 'device') return
        setTimeout(() => {
          if (!this.myNetwork) return
          const { x, y } = this.myNetwork.getViewPosition()
          this.eagelEyeVis &&
            this.eagelEyeVis.moveTo({
              position: {
                x: x,
                y: y
              },
              scale: this.myNetwork.getScale() * this.scale
            })
          showNodes && this.showNodes()
        })
      },
      getRoomList() {
        // 划分机房数据
        const roomList = []
        this.nodes.forEach((ele, index) => {
          if (ele.roomId) {
            const curRoom = roomList.find(j => j.roomId === ele.roomId)
            if (curRoom) {
              curRoom.nodes.push(ele)
            } else {
              roomList.push({
                roomId: ele.roomId,
                roomName: this.getRoomName(ele.roomId),
                nodes: [ele]
              })
            }
          }
        })
        this.roomList = roomList
        const _this = this
        setTimeout(() => {
          _this.setRoom()
        }, 300)
        this.myNetwork.on('dragEnd', properties => {
          this.myNetwork.unselectAll()
        })
        this.myNetwork.on('dragging', properties => {
          // 拖拽某个节点时，联动
          if (properties.nodes && properties.nodes.length) {
            // console.log('properties', properties)
            // 更新机房内节点位置
            const { x, y } = properties.pointer.canvas
            this.roomList.forEach(i => {
              i.nodes.forEach(j => {
                if (j.id === properties.nodes[0]) {
                  j.x = x
                  j.y = y
                }
              })
            })
          }
          this.setRoom()
        })
        this.myNetwork.on('zoom', parms => {
          // console.log('parms', parms)
          this.roomScale = parms.scale
          this.setRoom()
          if (this.$parent.clickProp) {
            this.$emit('dragStart')
          }
        })
      },
      // 添加机房框
      setRoom() {
        const scale = this.roomScale < 0.8 ? 0.8 : this.roomScale
        this.roomData = this.roomList.map(i => {
          const xArray = []
          const yArray = []
          i.nodes.forEach(j => {
            if (!j.x || !j.y) {
              const positions = this.myNetwork.getPositions(j.id)
              j.x = positions[j.id].x
              j.y = positions[j.id].y
            }
            const domLocation = this.myNetwork.canvasToDOM({ x: j.x, y: j.y })
            xArray.push(domLocation.x)
            yArray.push(domLocation.y)
          })
          // 通过机房内最小x、y的值来计算机房盒子大小和定位
          const x1 = Math.min(...xArray)
          const x2 = Math.max(...xArray)
          const y1 = Math.min(...yArray)
          const y2 = Math.max(...yArray)
          return {
            roomId: i.roomId,
            roomName: i.roomName,
            width: (x2 - x1 + 120) * scale + 'px',
            height: (y2 - y1 + 100) * scale + 'px',
            x: x1 - 40 * scale + 'px',
            y: y1 - 50 * scale + 'px'
          }
        })
        // console.log('roomData', this.roomData)
      },
      // 获取机房
      getRoomName(stationId) {
        const stationData = this.baseRegionList.find(i => i.regionId === stationId)
        return stationData ? stationData.regionName : ''
      },
      handleDrag() {
        // 鹰眼跟着拓扑联动
        this.myNetwork.on('dragStart', () => {
          this.dragCode = 1
          this.$emit('dragStart')
        })
        this.myNetwork.on('dragging', properties => {
          if (this.dragCode === 1) {
            // 拖拽整个拓扑图时，联动
            const { x, y } = this.myNetwork.getViewPosition()
            this.eagelEyeVis.moveTo({
              position: {
                x: x,
                y: y
              }
            })
            // 拖拽某个节点时，联动
            if (properties.nodes && properties.nodes.length) {
              const { x, y } = properties.pointer.canvas
              this.eagelEyeVis.moveNode(properties.nodes[0], x, y)
            }
          }
        })
        // 拓扑跟着鹰眼联动
        this.eagelEyeVis.on('dragStart', () => {
          this.dragCode = 2
        })
        this.eagelEyeVis.on('dragEnd', () => {
          this.dragCode = 0
        })
        this.eagelEyeVis.on('dragging', properties => {
          if (this.dragCode === 2) {
            const { x, y } = this.eagelEyeVis.getViewPosition()
            this.myNetwork.moveTo({
              position: {
                x: x,
                y: y
              }
            })
            // 拖拽某个节点时，联动
            // if (properties.nodes && properties.nodes.length) {
            //   const { x, y } = properties.pointer.canvas
            //   this.myNetwork.moveNode(properties.nodes[0], x, y)
            // }
          }
        })
      },
      handleZoom() {
        // 拓扑缩放
        // this.myNetwork.on('zoom', parms => {
        //   const { x, y } = this.myNetwork.getViewPosition()
        //   this.eagelEyeVis.moveTo({
        //     position: {
        //       x: x,
        //       y: y
        //     },
        //     scale: parms.scale * this.scale
        //   })
        // })
        // 鹰眼缩放
        // this.eagelEyeVis.on('zoom', parms => {
        //   const { x, y } = this.eagelEyeVis.getViewPosition()
        //   this.myNetwork.moveTo({
        //     position: {
        //       x: x,
        //       y: y
        //     },
        //     scale: parms.scale / this.scale
        //   })
        // })
      },
      edgesHover() {
        this.myNetwork.on('hoverEdge', properties => {
          this.clientX = properties.event.clientX
          this.clientY = properties.event.clientY
          this.edgesHoverFunc(properties.edge, 'HOVER')
        })
        this.myNetwork.on('blurEdge', properties => {
          const _this = this
          _this.edgesHoverFunc(properties.edge, 'LEAVE')
        })
      },
      edgesHoverFunc(curId, mouseStatus) {
        this.$emit('hoverEdge', {
          id: curId,
          mouseStatus: mouseStatus,
          mouseEvent: mouseStatus === 'HOVER' ? 'fiberHover' : mouseStatus,
          left: this.clientX, // 当前鼠标在画布中的位置(校准后)
          top: this.clientY - this.network_offset_top
        })
      },
      nodesHover() {
        this.myNetwork.on('hoverNode', properties => {
          this.clientX = properties.event.clientX
          this.clientY = properties.event.clientY
          this.nodesHoverFunc(properties.node, 'HOVER')
        })
        this.myNetwork.on('blurNode', properties => {
          this.nodesHoverFunc(properties.node, 'LEAVE')
        })
      },
      nodesHoverFunc(curId, mouseStatus) {
        const currentNode = this.nodes.find(i => i.id === curId) || {}
        this.$emit('hoverNode', {
          ...currentNode,
          mouseStatus: mouseStatus,
          mouseEvent: mouseStatus === 'HOVER' ? 'stationHover' : mouseStatus,
          left: this.clientX, // 当前鼠标在画布中的位置(校准后)
          top: this.clientY - this.network_offset_top
        })
      },
      edgesClick() {
        // 选中连接线
        this.myNetwork.on('selectEdge', properties => {
          console.log(properties)
          this.clientX = properties.event.center.x
          this.clientY = properties.event.center.y
          this.edges.forEach(ele => {
            if (ele.id === properties.edges[0]) {
              this.$emit('selectEdge', {
                id: properties.edges[0],
                mouseStatus: 'SELECT',
                mouseEvent: 'fiberClick',
                label: ele.label,
                left: this.clientX - 10,
                top: this.clientY - this.network_offset_top - 10,
                fromNodeId: ele.fromNodeId,
                toNodeId: ele.toNodeId
              })
            }
          })
        })
        // 取消选中连接线
        this.myNetwork.on('deselectEdge', properties => {
          if (!properties.edges.length) {
            // 单纯取消当前选中的连线，而不是从一个选中连线切换到另一个选中连线
            this.$emit('selectEdge', {
              id: properties.previousSelection.edges[0],
              mouseStatus: 'CANCEL',
              mouseEvent: 'fiberClick',
              left: 0,
              top: 0
            })
          }
        })
      },
      nodesClick() {
        // 选中节点
        this.myNetwork.on('selectNode', properties => {
          this.clientX = properties.event.center.x
          this.clientY = properties.event.center.y
          const currentNode = this.nodes.find(i => i.id === properties.nodes[0]) || {}
          this.$emit('selectNode', {
            ...currentNode,
            mouseEvent: 'stationClick',
            ...{
              id: properties.nodes[0],
              mouseStatus: 'SELECT',
              left: this.clientX,
              top: this.clientY - this.network_offset_top
            }
          })
        })
        // 取消选中节点
        this.myNetwork.on('deselectNode', properties => {
          if (!properties.nodes.length) {
            // 单纯取消当前选中的节点，而不是从一个选中节点切换到另一个选中节点
            this.$emit('selectNode', {
              id: properties.previousSelection.nodes[0],
              mouseStatus: 'CANCEL',
              mouseEvent: 'stationClick',
              left: 0,
              top: 0
            })
          }
        })
      },
      hightLightNode(nodeId) {
        // 根据节点id，将对应节点高亮展示
        const location = this.myNetwork.getPositions(nodeId)[nodeId] // 找到节点的位置
        this.myNetwork.moveTo({
          position: { x: location.x, y: location.y },
          animation: {
            duration: 1500,
            easingFunction: 'linear'
          }
        }) // 将拓扑中心移动到该节点
        // 鹰眼也需要跟着移动
        this.eagelEyeVis.moveTo({
          position: { x: location.x, y: location.y },
          animation: {
            duration: 1500,
            easingFunction: 'linear'
          }
        })
        this.nodes.forEach(j => {
          if (nodeId === j.id) {
            this.myNode.update([
              {
                id: nodeId,
                font: { color: colorData.highLight },
                icon: { color: colorData.highLight },
                image: categoryNode(j, 'highLight')
              }
            ])
            j.selectHighLight = {
              eventType: 'select',
              type: 'always',
              style: {
                color: colorData.highLight
              }
            }
          }
        })
      },
      removeHightLightNode(nodeId) {
        this.nodes.forEach(j => {
          if (nodeId === j.id) {
            let color = ''
            j.selectHighLight = {
              eventType: 'select',
              type: 'leave',
              style: {
                color: color
              }
            }
            // 取消高亮，需要判断当前节点的clickHighLight和hoverHighLight两种状态
            if (j.clickHighLight && j.clickHighLight.type === 'always') {
              // 如果当前站点处于被点击高亮状态
              color = j.clickHighLight.style.color
            } else if (j.hoverHighLight && j.hoverHighLight.type === 'always') {
              color = j.hoverHighLight.style.color
            } else {
              color = j.status === 'DAMAGE' ? colorData.damage : colorData.normal
            }
            this.myNode.update([
              {
                id: nodeId,
                font: { color: color },
                icon: { color: color },
                image: categoryNode(j)
              }
            ])
          }
        })
      },
      hoverNodes(data) {
        let size = 18
        let color = ''
        if (data.type === 'always' && data.color) {
          color = colorData[data.color]
        }
        data.nodeIds.forEach(ele => {
          const nodeData = this.nodes.find(i => i.nodeId === ele)
          if (nodeData) {
            if (data.eventType === 'click') {
              nodeData.clickHighLight = {
                type: data.type,
                style: {
                  color,
                  size
                }
              }
            }
            if (data.eventType === 'hover') {
              nodeData.hoverHighLight = {
                type: data.type,
                style: {
                  color,
                  size
                }
              }
            }
            if (data.type === 'leave') {
              if (nodeData.clickHighLight && nodeData.clickHighLight.type === 'always') {
                size = nodeData.clickHighLight.style.size
                color = nodeData.clickHighLight.style.color
              } else if (nodeData.hoverHighLight && nodeData.hoverHighLight.type === 'always') {
                size = nodeData.hoverHighLight.style.size
                color = nodeData.hoverHighLight.style.color
              } else if (nodeData.selectHighLight && nodeData.selectHighLight.type === 'always') {
                size = nodeData.selectHighLight.style.size
                color = nodeData.selectHighLight.style.color
              } else {
                size = 14 // 默认14
                color = nodeData.font.color || colorData.normal
              }
            }
            this.myNode.update([
              {
                id: nodeData.id,
                font: {
                  size: size,
                  color: color
                }
              }
            ])
          }
        })
      },
      /**
       * 链路高亮显示
       * @param {cableIds,type} data
       */
      hoverEdges(data) {
        console.log('hoverEdges', data)
        let size = 16
        let color = ''
        let width = 4
        if (data.type === 'always' && data.color) {
          color = colorData[data.color]
        }
        let opacity = 1
        data.cableIds.forEach(ele => {
          const item = this.edges.find(i => i.id === ele)
          if (item) {
            // 记录高亮状态
            item.highLightStatus = {
              type: data.type,
              font: {
                size,
                color
              },
              color: {
                color,
                opacity
              }
            }
            if (data.eventType === 'click') {
              item.clickHighLight = {
                type: data.type,
                style: {
                  color,
                  size,
                  width,
                  opacity
                }
              }
            }
            if (data.eventType === 'hover') {
              item.hoverHighLight = {
                type: data.type,
                style: {
                  color,
                  size,
                  width,
                  opacity
                }
              }
            }
            if (data.type === 'leave') {
              if (item.clickHighLight && item.clickHighLight.type === 'always') {
                size = item.clickHighLight.style.size
                color = item.clickHighLight.style.color
                opacity = item.clickHighLight.style.opacity
                width = item.clickHighLight.style.width
              } else if (item.hoverHighLight && item.hoverHighLight.type === 'always') {
                size = item.hoverHighLight.style.size
                color = item.hoverHighLight.style.color
                opacity = item.hoverHighLight.style.opacity
                width = item.hoverHighLight.style.width
              } else if (item.selectHighLight && item.selectHighLight.type === 'always') {
                size = 0
                color = item.font.color || colorData.normal
                opacity = item.color.opacity || 0.5
                width = 2
              } else {
                size = 0 // 隐藏光缆label
                color = item.font.color || colorData.normal
                opacity = item.color.opacity || 0.5
                width = 2
              }
            }
            this.myEdge.update([
              {
                id: ele,
                font: {
                  size: size,
                  color: color
                },
                color: {
                  color: color,
                  opacity: opacity
                },
                width: width
              }
            ])
          }
        })
        if (data.nodeIds) {
          this.hoverNodes(data)
        }
      },
      highLightStaionAndFiber(data) {
        const updateEdges = []
        this.edges.forEach(i => {
          const item = data.cableIds.includes(i.id)
            ? {
              id: i.id,
              font: {
                color: colorData[data.color],
                size: 16
              },
              color: {
                color: colorData[data.color],
                opacity: 1
              },
              width: 4
            }
            : {
              id: i.id,
              font: {
                color: i.color.color,
                size: 0
              },
              color: {
                color: i.color.color,
                opacity: i.color.opacity
              },
              width: 2
            }
          updateEdges.push(item)
        })
        this.myEdge.update(updateEdges)
        const updateNodes = []
        const activatedNodes = []
        this.nodes.forEach(i => {
          let color = i.font.color
          let highLight = null
          if (
            data.nodeIds.includes(i.nodeId) &&
            this.stationSelected.includes(i.nodeId)
          ) {
            activatedNodes.push(i.nodeId)
            color = colorData[data.color]
            highLight = 'highLight'
          }
          const item = {
            id: i.id,
            image: categoryNode(i, highLight),
            font: {
              color: color
            }
          }
          updateNodes.push(item)
        })
        this.myNode.update(updateNodes)
        // console.log('activatedNodes', activatedNodes)
        activatedNodes.length && this.fitView(activatedNodes)
      },
      // 控制站点的显示和隐藏
      showNodes() {
        const showNode = []
        this.nodes.forEach(i => {
          const visiable =
            this.stationSelected.includes(i.nodeId) &&
            (!i.alarmLevel.length || i.alarmLevel.some(j => !this.hideAlarmLevel.includes(j)))
          if (this.topoType !== 'device') {
            i = {
              ...i,
              id: i.id,
              hidden: !visiable,
              image: categoryNode(i),
              font: {
                color: nodeColor(i)
              }
            }
          }
          this.myNode.update(i)
          visiable && showNode.push(i)
        })

        this.showEagelEye &&
          this.eagelEyeVis.setData({
            nodes: showNode,
            edges: this.edges
          })
      },
      // 控制光缆的显示和隐藏
      showFibers() {
        const cableNodes = []
        this.edges.forEach(i => {
          if (this.cableLevelSelected.includes(i.cableLevel)) {
            cableNodes.push(i.fromNodeId)
            cableNodes.push(i.toNodeId)
          }
        })
        const ids =
          this.dictCableLevelList.length !== this.cableLevelSelected.length
            ? Array.from(new Set(cableNodes))
            : this.nodes.map(i => i.nodeId)
        this.$store.commit('setStationSelected', ids)
        this.$store.commit('setFilterStationIds', ids)
      },
      // 设置连线的样式
      setEdgeSmooth() {
        this.edges.forEach(i => {
          const edges = this.edges.filter(
            j =>
              (j.fromNodeId === i.fromNodeId && j.toNodeId === i.toNodeId) ||
              (j.fromNodeId === i.toNodeId && j.toNodeId === i.fromNodeId)
          )
          if (edges.length === 1) {
            this.myEdge.update({
              id: edges[0].id,
              smooth: {
                enabled: false
              }
            })
            this.myEagelEdge &&
              this.myEagelEdge.update({
                id: edges[0].id,
                smooth: {
                  enabled: false
                }
              })
          }
        })
      },
      destroy() {
        if (this.myNetwork) {
          this.myNetwork.destroy()
          this.myNetwork = null
          this.myNode = null
          this.myEdge = null
        }
        if (this.eagelEyeVis) {
          this.eagelEyeVis.destroy()
          this.eagelEyeVis = null
        }
      },
      fitView(data, times = 0) {
        if (this.nodes.length === 0 && times < 4) {
          setTimeout(() => {
            this.fitView(data, times++)
          }, 500)
          return
        }
        const fitNodes = this.nodes.filter(i => data.includes(i.nodeId)).map(i => i.id)
        setTimeout(() => {
          if (!this.myNetwork) return
          this.myNetwork.fit({ nodes: fitNodes })
          const scale = this.myNetwork.getScale()
          !this.fullScreen &&
            this.myNetwork.moveTo({
              offset: { x: 0, y: 0 - this.staticsItem.height / 2 - 20 },
              scale: scale * 0.8
            })
        }, 500)
      },
      setEditable(newVal) {
        this.myNetwork.setOptions({
          interaction: {
            dragNodes: newVal
          }
        })
      }
    }
  }
</script>
<style lang="scss" scoped>
  @import '@/assets/css/mixin.module.scss';
  .network-wrap {
    height: 100%;
    // position: relative;
    .eagelEyeVis {
      position: absolute;
      right: 5px;
      bottom: 5px;
      @include themeify {
        border: 1px solid themed('bigBorderColor');
      }
    }
    .icon-switch {
      position: absolute;
      right: 20px;
      top: 80px;
    }
  }
  .room-box {
    position: absolute;
    pointer-events: none;
    @include themeify {
      border: 2px dashed themed('textColor');
      color: themed('textColor');
    }
  }
</style>
